/**
 * Formatting utilities for HTML cleaning and code snippet formatting
 */

export class FormattingUtils {
  // HTML entities mapping for comprehensive cleaning
  private static readonly HTML_ENTITIES: Record<string, string> = {
    '&quot;': '"',
    '&amp;': '&',
    '&lt;': '<',
    '&gt;': '>',
    '&nbsp;': ' ',
    '&apos;': "'",
    '&cent;': '¢',
    '&pound;': '£',
    '&yen;': '¥',
    '&euro;': '€',
    '&copy;': '©',
    '&reg;': '®',
    '&#39;': "'",
    '&#x27;': "'",
    '&#x2F;': '/',
    '&#x60;': '`',
    '&#x3D;': '='
  };

  // Common dangerous HTML patterns to remove
  private static readonly DANGEROUS_PATTERNS = [
    /<script[^>]*>.*?<\/script>/gis,
    /<style[^>]*>.*?<\/style>/gis,
    /<iframe[^>]*>.*?<\/iframe>/gis,
    /<object[^>]*>.*?<\/object>/gis,
    /<embed[^>]*>/gi,
    /<link[^>]*>/gi,
    /<meta[^>]*>/gi,
    /on\w+\s*=\s*["'][^"']*["']/gi,
    /javascript:\s*[^"']*/gi
  ];

  /**
   * Extract clean text from HTML snippet by removing tags and entities
   */
  static extractTextFromHtml(htmlSnippet: string): string {
    if (!htmlSnippet || typeof htmlSnippet !== 'string') {
      return '';
    }

    let cleanText = htmlSnippet;

    // Remove dangerous patterns first
    for (const pattern of this.DANGEROUS_PATTERNS) {
      cleanText = cleanText.replace(pattern, '');
    }

    // Remove HTML comments
    cleanText = cleanText.replace(/<!--[\s\S]*?-->/g, '');

    // Remove HTML tags but preserve content
    cleanText = cleanText.replace(/<[^>]+>/g, '');

    // Replace HTML entities
    for (const [entity, replacement] of Object.entries(this.HTML_ENTITIES)) {
      cleanText = cleanText.replace(new RegExp(entity, 'g'), replacement);
    }

    // Replace numeric HTML entities
    cleanText = cleanText.replace(/&#(\d+);/g, (_, dec) => {
      return String.fromCharCode(dec);
    });

    // Replace hex HTML entities
    cleanText = cleanText.replace(/&#x([0-9a-f]+);/gi, (_, hex) => {
      return String.fromCharCode(parseInt(hex, 16));
    });

    // Clean up whitespace
    cleanText = cleanText
      .replace(/\s+/g, ' ') // Normalize whitespace
      .replace(/\n\s*\n/g, '\n') // Remove empty lines
      .trim();

    return cleanText;
  }

  /**
   * Extract line numbers from HTML snippet with multiple strategies
   */
  static extractLineNumbers(htmlSnippet: string): number[] {
    const lineNumbers: number[] = [];
    
    if (!htmlSnippet || typeof htmlSnippet !== 'string') {
      return lineNumbers;
    }

    // Strategy 1: Look for data-line attributes
    const dataLineMatches = htmlSnippet.match(/data-line="(\d+)"/g);
    if (dataLineMatches) {
      for (const match of dataLineMatches) {
        const numberMatch = match.match(/data-line="(\d+)"/);
        if (numberMatch) {
          lineNumbers.push(parseInt(numberMatch[1], 10));
        }
      }
    }

    // Strategy 2: Look for line number classes or IDs
    const lineClassMatches = htmlSnippet.match(/class="[^"]*line[^"]*(\d+)[^"]*"/g);
    if (lineClassMatches && lineNumbers.length === 0) {
      for (const match of lineClassMatches) {
        const numberMatch = match.match(/(\d+)/);
        if (numberMatch) {
          lineNumbers.push(parseInt(numberMatch[1], 10));
        }
      }
    }

    // Strategy 3: Look for line spans with numbers
    const lineSpanMatches = htmlSnippet.match(/<span[^>]*>(\d+)<\/span>/g);
    if (lineSpanMatches && lineNumbers.length === 0) {
      for (const match of lineSpanMatches) {
        const numberMatch = match.match(/>(\d+)</);
        if (numberMatch) {
          const lineNum = parseInt(numberMatch[1], 10);
          if (lineNum > 0 && lineNum < 10000) { // Reasonable line number range
            lineNumbers.push(lineNum);
          }
        }
      }
    }

    // Remove duplicates and sort
    return [...new Set(lineNumbers)].sort((a, b) => a - b);
  }

  /**
   * Map file extension to programming language for syntax highlighting
   */
  static getLanguageFromExtension(extension: string): string {
    if (!extension || typeof extension !== 'string') {
      return 'text';
    }

    const extensionMap: Record<string, string> = {
      // Web technologies
      'js': 'javascript',
      'jsx': 'javascript',
      'ts': 'typescript',
      'tsx': 'typescript',
      'html': 'html',
      'htm': 'html',
      'css': 'css',
      'scss': 'scss',
      'sass': 'sass',
      'less': 'less',
      'vue': 'vue',
      'svelte': 'svelte',
      
      // Backend languages
      'py': 'python',
      'pyw': 'python',
      'ipynb': 'python',
      'java': 'java',
      'c': 'c',
      'cpp': 'cpp',
      'cc': 'cpp',
      'cxx': 'cpp',
      'c++': 'cpp',
      'h': 'c',
      'hpp': 'cpp',
      'hxx': 'cpp',
      'cs': 'csharp',
      'php': 'php',
      'rb': 'ruby',
      'go': 'go',
      'rs': 'rust',
      'swift': 'swift',
      'kt': 'kotlin',
      'kts': 'kotlin',
      'scala': 'scala',
      'clj': 'clojure',
      'cljs': 'clojure',
      'hs': 'haskell',
      'elm': 'elm',
      'erl': 'erlang',
      'ex': 'elixir',
      'exs': 'elixir',
      'dart': 'dart',
      
      // Shell and scripting
      'sh': 'bash',
      'bash': 'bash',
      'zsh': 'bash',
      'fish': 'bash',
      'ps1': 'powershell',
      'psm1': 'powershell',
      'bat': 'batch',
      'cmd': 'batch',
      
      // Data and config
      'json': 'json',
      'yaml': 'yaml',
      'yml': 'yaml',
      'toml': 'toml',
      'ini': 'ini',
      'cfg': 'ini',
      'conf': 'ini',
      'xml': 'xml',
      'sql': 'sql',
      'graphql': 'graphql',
      'gql': 'graphql',
      
      // Documentation
      'md': 'markdown',
      'markdown': 'markdown',
      'rst': 'restructuredtext',
      'tex': 'latex',
      'adoc': 'asciidoc',
      
      // Scientific and statistical
      'r': 'r',
      'R': 'r',
      'matlab': 'matlab',
      'm': 'matlab',
      
      // Other languages
      'pl': 'perl',
      'pm': 'perl',
      'lua': 'lua',
      'vim': 'vim',
      'lisp': 'lisp',
      'scm': 'scheme',
      'ml': 'ocaml',
      'fs': 'fsharp',
      'pas': 'pascal',
      'asm': 'assembly',
      's': 'assembly',
      
      // Build and deployment
      'dockerfile': 'dockerfile',
      'makefile': 'makefile',
      'make': 'makefile',
      'cmake': 'cmake',
      'gradle': 'gradle',
      'ant': 'xml',
      'pom': 'xml'
    };

    const normalizedExt = extension.toLowerCase().replace(/^\./, '');
    return extensionMap[normalizedExt] || 'text';
  }

  /**
   * Get language from file path
   */
  static getLanguageFromPath(filePath: string): string {
    if (!filePath || typeof filePath !== 'string') {
      return 'text';
    }

    // Handle special cases first
    const fileName = filePath.split('/').pop()?.toLowerCase() || '';
    
    // Special file names
    const specialFiles: Record<string, string> = {
      'dockerfile': 'dockerfile',
      'makefile': 'makefile',
      'rakefile': 'ruby',
      'gemfile': 'ruby',
      'podfile': 'ruby',
      'vagrantfile': 'ruby',
      'gruntfile.js': 'javascript',
      'gulpfile.js': 'javascript',
      'webpack.config.js': 'javascript',
      'package.json': 'json',
      'composer.json': 'json',
      'tsconfig.json': 'json',
      '.eslintrc': 'json',
      '.babelrc': 'json'
    };

    if (specialFiles[fileName]) {
      return specialFiles[fileName];
    }

    // Extract extension
    const extension = filePath.split('.').pop();
    return extension ? this.getLanguageFromExtension(extension) : 'text';
  }

  /**
   * Format code snippet with syntax highlighting markers and proper structure
   */
  static formatCodeSnippet(snippet: string, language: string, maxLength: number = 500, maxLines: number = 10): string {
    if (!snippet || typeof snippet !== 'string' || !snippet.trim()) {
      return '';
    }

    let processedSnippet = snippet.trim();
    
    // Limit snippet length to prevent overwhelming output
    if (processedSnippet.length > maxLength) {
      processedSnippet = processedSnippet.substring(0, maxLength) + '...';
    }

    // Split into lines and process
    const lines = processedSnippet.split('\n');
    const formattedLines: string[] = [];
    let emptyLineCount = 0;

    for (let i = 0; i < lines.length && formattedLines.length < maxLines; i++) {
      const line = lines[i];
      const trimmedLine = line.trim();
      
      // Skip excessive empty lines
      if (!trimmedLine) {
        emptyLineCount++;
        if (emptyLineCount <= 2) { // Allow max 2 consecutive empty lines
          formattedLines.push('');
        }
        continue;
      }
      
      emptyLineCount = 0;
      
      // Preserve indentation but clean up the line
      const cleanedLine = line.trimEnd();
      formattedLines.push(cleanedLine);
    }

    // Add truncation indicator if needed
    if (lines.length > maxLines) {
      formattedLines.push('... (truncated)');
    }

    // Join lines
    const formattedSnippet = formattedLines.join('\n').trim();

    // Add language marker for syntax highlighting
    if (language && language !== 'text' && formattedSnippet) {
      return `\`\`\`${language}\n${formattedSnippet}\n\`\`\``;
    }
    
    return formattedSnippet;
  }

  /**
   * Truncate text with smart word boundary detection
   */
  static truncateText(text: string, maxLength: number, suffix: string = '...'): string {
    if (!text || typeof text !== 'string') {
      return '';
    }

    if (text.length <= maxLength) {
      return text;
    }

    // Try to break at word boundary
    const truncated = text.substring(0, maxLength - suffix.length);
    const lastSpace = truncated.lastIndexOf(' ');
    
    if (lastSpace > maxLength * 0.8) { // Only use word boundary if it's not too far back
      return truncated.substring(0, lastSpace) + suffix;
    }
    
    return truncated + suffix;
  }

  /**
   * Clean and normalize whitespace in text
   */
  static normalizeWhitespace(text: string): string {
    if (!text || typeof text !== 'string') {
      return '';
    }

    return text
      .replace(/\r\n/g, '\n') // Normalize line endings
      .replace(/\r/g, '\n')   // Handle old Mac line endings
      .replace(/\t/g, '  ')   // Convert tabs to spaces
      .replace(/[ \t]+$/gm, '') // Remove trailing whitespace
      .replace(/\n{3,}/g, '\n\n') // Limit consecutive newlines
      .trim();
  }

  /**
   * Extract context around a match in text
   */
  static extractContext(text: string, searchTerm: string, contextLines: number = 2): string {
    if (!text || !searchTerm) {
      return text;
    }

    const lines = text.split('\n');
    const matchingLines: number[] = [];
    
    // Find lines containing the search term
    lines.forEach((line, index) => {
      if (line.toLowerCase().includes(searchTerm.toLowerCase())) {
        matchingLines.push(index);
      }
    });

    if (matchingLines.length === 0) {
      return text;
    }

    // Collect context lines
    const contextSet = new Set<number>();
    matchingLines.forEach(lineIndex => {
      for (let i = Math.max(0, lineIndex - contextLines); 
           i <= Math.min(lines.length - 1, lineIndex + contextLines); 
           i++) {
        contextSet.add(i);
      }
    });

    // Build result with context
    const contextLines_array = Array.from(contextSet).sort((a, b) => a - b);
    const result: string[] = [];
    let lastIndex = -2;

    contextLines_array.forEach(index => {
      if (index > lastIndex + 1) {
        if (result.length > 0) {
          result.push('...');
        }
      }
      result.push(lines[index]);
      lastIndex = index;
    });

    return result.join('\n');
  }
}

/**
 * Response formatting utilities
 */
export class ResponseFormatter {
  /**
   * Format search results summary
   */
  static formatSummary(totalResults: number, resultsShown: number, repositoriesFound: number): string {
    const parts = [
      `📊 **Search Results Summary**`,
      `• Total matches found: **${totalResults.toLocaleString()}**`,
      `• Results shown: **${resultsShown.toLocaleString()}**`,
      `• Repositories searched: **${repositoriesFound.toLocaleString()}**`
    ];

    return parts.join('\n');
  }

  /**
   * Format language statistics
   */
  static formatLanguageStats(languages: Array<{language: string, count: number}>): string {
    if (!languages || languages.length === 0) {
      return '';
    }

    const header = '🔤 **Top Languages:**';
    const items = languages
      .slice(0, 5) // Show top 5
      .map(lang => `• ${lang.language}: ${lang.count.toLocaleString()} matches`)
      .join('\n');

    return `${header}\n${items}`;
  }

  /**
   * Format repository statistics
   */
  static formatRepositoryStats(repositories: Array<{repository: string, count: number}>): string {
    if (!repositories || repositories.length === 0) {
      return '';
    }

    const header = '📁 **Top Repositories:**';
    const items = repositories
      .slice(0, 5) // Show top 5
      .map(repo => `• ${repo.repository}: ${repo.count.toLocaleString()} matches`)
      .join('\n');

    return `${header}\n${items}`;
  }

  /**
   * Format file match entry
   */
  static formatFileMatch(
    filePath: string, 
    repository: string, 
    branch: string, 
    totalMatches: number, 
    lineNumbers: number[], 
    codeSnippet: string,
    language: string
  ): string {
    const parts = [
      `### 📄 \`${filePath}\``,
      `**Repository:** ${repository}`,
      `**Branch:** ${branch}`,
      `**Matches:** ${totalMatches}`,
      lineNumbers.length > 0 ? `**Lines:** ${lineNumbers.join(', ')}` : '',
      language !== 'text' ? `**Language:** ${language}` : '',
      '',
      codeSnippet
    ].filter(Boolean);

    return parts.join('\n');
  }
}
